package comtacti.native.enums;
#if macro
import macrotools.Imports;
import macrotools.MacroTools;
import macrotools.TypeImports;

using Lambda;
#end

/**
 * ValuedEnum :	Auto-Build class that generates an enum out of the static vars from the class.
 *  The class will then be , and an enum will be created with the same path.
 *  
 * 
 * @langversion		HaXe 2
 * @targets			cpp, flash, flash9, js, neko, php
 * @author			Caue Waneck:<a href="mailto">waneck@gmail.com</a>
 * @since			2011-10-21
 */
@:autoBuild(comtacti.native.internal.enums.ValuedEnumBuild.build()) class ValuedEnum
{
	public static function and<T>(arr:Array<T>, obj:T):Array<T>
	{
		arr.push(obj);
		return arr;
	}
	
	@:macro public static function toValue(_enum:Expr):Expr
	{
		return _toValue(_enum);
	}
	
	@:macro public static function toBitEnum(_enum:Expr):Expr
	{
		var t = Context.typeof(_enum);
		switch(Context.follow(t))
		{
			case TEnum(e, _):
				if (comtacti.native.internal.enums.ValuedEnumBuild.isValuedEnum(e))
					return {expr:EArrayDecl([_enum]), pos:_enum.pos};
			default:
		}
		
		throw new Error("Type is not a valued Bit Enum.", _enum.pos);
	}
	
#if macro
	private static function _toValue(_enum:Expr, ?type:Type):Expr
	{
		var t = (type != null) ? type : Context.follow(Context.typeof(_enum));
		var pos = _enum.pos;
		
		switch(t)
		{
			case TEnum(e, _):
				var e = e.get();
				return {expr:EArray(comtacti.native.internal.enums.ValuedEnumBuild.getEnumExpr(e, pos), MacroTools.mkCall(["Type", "enumIndex"], [_enum], pos)), pos:pos};
			case TInst(i, p):
				if (i.toString() != "Array") throw new Error("This is not a valid enum type.", pos);
				var eValues = switch(Context.follow(p[0]))
				{
					case TEnum(e, _):
						comtacti.native.internal.enums.ValuedEnumBuild.getEnumExpr(e.get(), pos);
					default:
						throw new Error("This is not a valid enum type.", pos);
						null;
				};
				
				var ret = {
					expr:EBlock([
						{expr: EVars([{type:null, name:"ret", expr:{expr:EConst(CInt("0")), pos:pos}}]), pos:pos},
						{expr: EFor({expr:EIn({expr:EConst(CIdent("_en")), pos:pos}, _enum), pos:pos}, 
							{expr:EBinop(OpAssignOp(OpOr),
								{expr:EConst(CIdent("ret")), pos:pos}, 
								{expr:EArray(eValues, MacroTools.mkCall(["Type", "enumIndex"], [{expr:EConst(CIdent("_en")), pos:pos}], pos)), pos:pos}
								),
							pos:pos}), pos:pos},
						{expr:EConst(CIdent("ret")), pos:pos}
					]),
					pos:pos
				};
				return ret;
				
			default: throw new Error("This is not a valid enum type.", pos);
		}
		
		throw "assert";
		return null;
	}
#end
}